package com.yeban.pattem.proxy.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @Desc Cglib代理
 * @Author yeban
 * @Date 2025/11/6
 *
 * 一、概念
 * 在内存中构建一个子类对象从而实现对目标对象功能的扩展
 *
 * 二、优缺点
 * 优点：
 *      1.目标对象可以不实现接口
 *      2.可以在不改变原有的目标方法的基础上，实现对其增强功能
 *      3.实现1解耦，实现代码的扩展性
 * 缺点：
 *      实现MethodInterceptor 接口, 重写拦截器intercept()
 *
 * 三、实现步骤
 *      1. 引入jar包
 *      2. 代理类中实现 MethodInterceptor 接口，重写拦截器 intercept()
 *      3. 提供一个获得代理对象的方法
 *
 * 四、特殊点
 *      1. 必须在目标类中，提供一个公开的无参的构造方法，否则会报:
 *          IllegalArgumentException: Superclass has no null constructors but no arguments were given
 *      2. 不可以将目标类，设置为final类，将无法在内存中构建子类对象
 *      3. 不可以将目标类的方法，使用final修饰, 这样会导致拦截器的intercept()失效
 *      4. 不可以将目标类中的方法，使用static修饰, 这样会导致拦截器的intercept()失效
 */
public class CglibProxyBankLoginService implements MethodInterceptor {

    private BankService target;

    public CglibProxyBankLoginService(BankService target) {
        this.target = target;
    }

    // 在内存中创建一个BankService的子类对象
    public BankService getProxy() {
        // 创建Enhancer实例
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(BankService.class);
        // 设置回调 this就是MenthodInterceptor拦截器，从而让intercept()生效
        enhancer.setCallback(this);
        // 构造代理对象
        BankService proxy = (BankService) enhancer.create();
        return proxy;
    }

    /**
     * 拦截器：非业务逻辑 + 执行目标方法
     * @param proxy         代理对象（一般不用）
     * @param method        目标方法对象
     * @param args          目标方法的形参列表哦哦
     * @param methodProxy   方法代理对象（一般不用）
     * @return  目标方法的执行的返回值，若目标方法没有返回值则为null
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();
        System.out.println("CGLIB日志开始追踪：The Method " + methodName + "begin with " + Arrays.toString(args));
        long l1 = System.currentTimeMillis();
        // 执行目标方法 第一个参数：目标对象    第二个参数：方法的形参列表
        // 返回值：目标方法的返回值，若目标方法没有返回值为null
        Object result = method.invoke(target, args);
        long l2 = System.currentTimeMillis();
        System.out.println("CGLIB日志追踪结束：The Method " + methodName + "end with , 耗时：" + (l2-l1) + "ms");
        return result;
    }
}
